﻿using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace _2DEngine
{
    public enum MouseButtons { Left, Right, Middle, X1, X2 };
    public class MouseDevice : InputDevice<MouseState>
    {
        MouseState last;
        MouseState current;
        MouseButtons[] currentButtons;

        public override MouseState State { get { return current; } }
        public MouseButtons[] PressedButtons { get { return currentButtons; } }

        Vector2 position;
        public Vector2 Position
        {
            get { return position; }
            set 
            { 
                position = value; 
                Mouse.SetPosition((int)value.X, (int)value.Y); 
            }
        }
        public Vector2 Delta = Vector2.Zero;

        public bool ResetMouseAfterUpdate = true;
        public float ScrollPosition { get { return current.ScrollWheelValue; } }
        public float ScrollDelta = 0;

        public event InputEventHandler<MouseButtons, MouseState> ButtonPressed;
        public event InputEventHandler<MouseButtons, MouseState> ButtonReleased;
        public event InputEventHandler<MouseButtons, MouseState> ButtonHeld;

        public MouseDevice()
        {
            if (ResetMouseAfterUpdate)
                Position = new Vector2(
                    Engine.GraphicsDevice.Viewport.Width / 2,
                    Engine.GraphicsDevice.Viewport.Height / 2);

            current = Mouse.GetState();
            Update();
        }

        List<MouseButtons> pressed = new List<MouseButtons>();

        public override void Update()
        {
            last = current;
            float lastScrollWheel = last.ScrollWheelValue;
            
            current = Mouse.GetState();
            pressed.Clear();

            ScrollDelta = ScrollPosition - lastScrollWheel;

            if (ResetMouseAfterUpdate)
            {
                Vector2 center = new Vector2(
                    Engine.GraphicsDevice.Viewport.Width / 2,
                    Engine.GraphicsDevice.Viewport.Height / 2);

                Delta = new Vector2(current.X - center.X, current.Y - center.Y);
                Position = center;
            }
            else
                Delta = new Vector2(current.X - last.X, current.Y - last.Y);

            foreach (MouseButtons button in Util.GetEnumValues<MouseButtons>())
            {
                if (IsButtonDown(button))
                    pressed.Add(button);

                if (WasButtonPressed(button))
                    if (ButtonPressed != null)
                        ButtonPressed(this, new InputDeviceEventArgs
                            <MouseButtons, MouseState>(button, this));

                if (WasButtonReleased(button))
                    if (ButtonReleased != null)
                        ButtonReleased(this, new InputDeviceEventArgs
                            <MouseButtons, MouseState>(button, this));

                if (WasButtonHeld(button))
                    if (ButtonHeld != null)
                        ButtonHeld(this, new InputDeviceEventArgs
                            <MouseButtons, MouseState>(button, this));
            }

            currentButtons = pressed.ToArray();
        }

        public bool IsButtonDown(MouseButtons Button)
        {
            return IsButtonDown(Button, current);
        }

        bool IsButtonDown(MouseButtons Button, MouseState State)
        {
            return GetButtonState(Button, State) == 
                ButtonState.Pressed ? true : false;
        }

        public bool IsButtonUp(MouseButtons Button)
        {
            return IsButtonUp(Button, current);
        }

        bool IsButtonUp(MouseButtons Button, MouseState State)
        {
            return GetButtonState(Button, State) == 
                ButtonState.Released ? true : false;
        }

        public bool WasButtonPressed(MouseButtons Button)
        {
            if (IsButtonUp(Button, last) && IsButtonDown(Button, current))
                return true;

            return false;
        }

        public bool WasButtonReleased(MouseButtons Button)
        {
            if (IsButtonDown(Button, last) && IsButtonUp(Button, current))
                return true;

            return false;
        }

        public bool WasButtonHeld(MouseButtons Button)
        {
            if (IsButtonDown(Button, last) && IsButtonDown(Button, current))
                return true;

            return false;
        }

        ButtonState GetButtonState(MouseButtons Button, MouseState State)
        {
            if (Button == MouseButtons.Left)
                return State.LeftButton;
            else if (Button == MouseButtons.Middle)
                return State.MiddleButton;
            else if (Button == MouseButtons.Right)
                return State.RightButton;
            else if (Button == MouseButtons.X1)
                return State.XButton1;
            else if (Button == MouseButtons.X2)
                return State.XButton2;

            return ButtonState.Released;
        }
    }
}
